package ping

import (
	"errors"
	"fmt"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"log"
	"math/rand"
	"net"
	"ping-info-go/internal/store"
	"ping-info-go/pkg/global"
	"sync"
	"time"
)

func RunPing(initPing bool) {
	log.Println("Start RunPing")

	store.PingStatus.Restart()

	// 等待上次完成
	<-store.PingStatus.FinishChan
	// 重置关闭状态和通道
	store.PingStatus.ContinuePing()

	if initPing {
		// 初始化数据
		store.TableDataList = make([]*store.TableData, len(make([]*store.TableData, 0)))
		for _, service := range global.Services {
			// 初始化数据
			tableData := &store.TableData{
				Name: service.Name,
				Host: service.Host,
				Mu:   &sync.Mutex{},
			}
			store.TableDataList = append(store.TableDataList, tableData)
		}
	}

	go servicePingFor()
}

func servicePingFor() {

	thisPingRound := store.PingStatus.AddPingRound()
	tick := time.Tick(time.Second)

	for thisRangeTimes := 0; thisRangeTimes < global.TimesPerInterval; thisRangeTimes++ {
		select {
		case <-tick:
			for idx, tableData := range store.TableDataList {
				global.PingWg.Add(1)
				go servicePing(tableData, idx+1)
			}
		case <-store.PingStatus.PauseChan:
			global.PingWg.Wait()
			<-store.PingStatus.PauseChan
		case <-store.PingStatus.StopChan:
			global.PingWg.Wait()
			break
		}
	}

	global.PingWg.Wait()
	store.PingStatus.DoFinish()

	// 开启定时任务触发下一次运行
	go store.PingStatus.SetNextTickContinue(thisPingRound, RunPing)
}

//
//func servicePingFor2(tableData *store.TableData, seq int) {
//	tick := time.Tick(time.Second)
//	for {
//		select {
//		case <-tick:
//			global.PingWg.Add(1)
//			go servicePing(tableData, seq)
//		case <-store.PingStatus.PauseChan:
//			<-store.PingStatus.ContinueChan
//		}
//	}
//}

func servicePing(tableData *store.TableData, seq int) {
	defer global.PingWg.Done()

	ipAddr, delay, err := ping(tableData.IP, tableData.Host, seq)

	// 写保护
	tableData.Mu.Lock()
	defer tableData.Mu.Unlock()

	tableData.Delay = delay
	tableData.TotalNum += 1
	if tableData.IP == "" {
		tableData.IP = ipAddr
	}

	if tableData.SuccessNum == 1 {
		tableData.AvgDelay = delay
		tableData.MinDelay = delay
		tableData.MaxDelay = delay
	}

	if err != nil {
		tableData.Status = err.Error()
		tableData.FailNum += 1
		//log.Printf("service %s ping seq:%d failed, err: %v", tableData.Name, seq, err)
		return
	}

	tableData.AvgDelay = time.Duration((tableData.AvgDelay.Nanoseconds()*tableData.SuccessNum + delay.Nanoseconds()) / (tableData.SuccessNum + 1))

	if delay < tableData.MinDelay {
		tableData.MinDelay = delay
	}
	if delay > tableData.MaxDelay {
		tableData.MaxDelay = delay
	}

	tableData.Status = "成功"
	tableData.SuccessNum += 1
}

// 发送ICMP Echo请求并返回往返时间
func ping(ipAddr, target string, seq int) (string, time.Duration, error) {
	var err error
	if "" == ipAddr {
		ipAddr, err = resolveTarget(target)
		if err != nil {
			return ipAddr, 0, fmt.Errorf("无法解析目标: %v", err)
		}
	}

	c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
	if err != nil {
		return ipAddr, 0, fmt.Errorf("无法创建ICMP连接: %v", err)
	}
	defer func(c *icmp.PacketConn) {
		_ = c.Close()
	}(c)

	id := rand.Intn(65535)

	msg := icmp.Message{
		Type: ipv4.ICMPTypeEcho,
		Code: 0,
		Body: &icmp.Echo{
			ID:   id, // 使用随机ID
			Seq:  seq,
			Data: []byte(global.PingData), // 使用常量数据
		},
	}

	data, err := msg.Marshal(nil)
	if err != nil {
		return ipAddr, 0, fmt.Errorf("无法序列化ICMP消息: %v", err)
	}

	sendTime := time.Now()

	_, err = c.WriteTo(data, &net.IPAddr{IP: net.ParseIP(ipAddr)})
	if err != nil {
		return ipAddr, 0, fmt.Errorf("发送ICMP请求失败: %v", err)
	}

	deadline := sendTime.Add(global.PingTimeout)
	if err := c.SetReadDeadline(deadline); err != nil {
		return ipAddr, 0, fmt.Errorf("设置ICMP超时失败: %v", err)
	}

	for {
		buf := make([]byte, 1500)
		n, _, err := c.ReadFrom(buf)
		if err != nil {
			var netErr net.Error
			if errors.As(err, &netErr) && netErr.Timeout() {
				return ipAddr, 0, fmt.Errorf("接收响应超时")
			}
			return ipAddr, 0, fmt.Errorf("接收响应失败: %v", err) // 其他错误（如权限、网络中断）
		}

		rtt := time.Since(sendTime)
		resp, err := icmp.ParseMessage(global.ProtocolICMP, buf[:n])
		if err != nil {
			continue
		}

		switch resp.Type {
		case ipv4.ICMPTypeEchoReply:
			// 验证ID和Seq匹配
			if echoReply, ok := resp.Body.(*icmp.Echo); ok && echoReply.ID == id && echoReply.Seq == seq {
				return ipAddr, rtt, nil
			}
		case ipv4.ICMPTypeDestinationUnreachable:
			return ipAddr, 0, fmt.Errorf("目标不可达")
		case ipv4.ICMPTypeTimeExceeded:
			return ipAddr, 0, fmt.Errorf("TTL过期（超时）")
		default:
			continue
		}
	}
}

// 解析目标为IP地址（处理域名情况）
func resolveTarget(target string) (string, error) {
	// 特殊地址处理列表
	switch target {
	case "localhost", "127.0.0.1":
		return "127.0.0.1", nil
	case "0.0.0.0":
		return "", errors.New("无效地址: 0.0.0.0 无法ping通")
	}

	ips, err := net.LookupIP(target)
	if err != nil {
		return "", err
	}

	// 优先选择IPv4地址
	for _, ip := range ips {
		if ip.To4() != nil {
			return ip.String(), nil
		}
	}

	// 如果没有IPv4地址，使用第一个IPv6地址
	if len(ips) > 0 {
		return ips[0].String(), nil
	}

	return "", errors.New("无法解析到IP地址")
}
